package icu.d4peng.cloud.common.http;

import com.dtflys.forest.config.ForestConfiguration;
import com.dtflys.forest.config.SpringForestProperties;
import com.dtflys.forest.http.ForestAddress;
import com.dtflys.forest.interceptor.SpringInterceptorFactory;
import com.dtflys.forest.logging.ForestLogHandler;
import com.dtflys.forest.reflection.SpringForestObjectFactory;
import com.dtflys.forest.scanner.ClassPathClientScanner;
import com.dtflys.forest.spring.ConverterBeanListener;
import com.dtflys.forest.spring.ForestBeanProcessor;
import com.dtflys.forest.ssl.SpringSSLKeyStore;
import icu.d4peng.cloud.common.http.properties.HttpProperties;
import icu.d4peng.cloud.common.http.properties.HttpSSLKeyStoreProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.boot.autoconfigure.AutoConfigurationPackages;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ResourceLoader;
import org.springframework.util.StringUtils;

/**
 * @author d4peng
 * @version 1.0.0
 * @date 2021-12-30 17:04
 * @description HttpAutoConfiguration:Http自动配置
 */
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties({HttpProperties.class})
@ConditionalOnProperty(prefix = HttpProperties.PREFIX, value = {"enabled"}, matchIfMissing = true, havingValue = "true")
public class HttpAutoConfiguration {
    private static final Logger LOGGER = LoggerFactory.getLogger(HttpAutoConfiguration.class);
    private final HttpProperties httpProperties;

    @Autowired
    public HttpAutoConfiguration(HttpProperties httpProperties) {
        this.httpProperties = httpProperties;
    }

    @Bean
    @ConditionalOnMissingBean
    public ForestBeanProcessor forestBeanProcessor() {
        return new ForestBeanProcessor();
    }

    /**
     * 配置属性的Spring环境下的实现
     *
     * @return 上下文属性配置
     */
    @Bean
    public SpringForestProperties springForestProperties() {
        return new SpringForestProperties();
    }

    /**
     * 通过Spring上下文获取Bean
     *
     * @return 对象工厂缓存
     */
    @Bean
    public SpringForestObjectFactory springForestObjectFactory() {
        return new SpringForestObjectFactory();
    }

    /**
     * Spring拦截器
     *
     * @return 拦截器接口
     */
    @Bean
    public SpringInterceptorFactory springInterceptorFactory() {
        return new SpringInterceptorFactory();
    }

    /**
     * 注册Configuration
     *
     * @param springForestProperties    配置属性的Spring环境下的实现
     * @param springForestObjectFactory 通过Spring上下文获取Bean
     * @param springInterceptorFactory  Spring拦截器
     * @return Configuration配置
     */
    @Bean
    public ForestConfiguration forestConfiguration(SpringForestProperties springForestProperties, SpringForestObjectFactory springForestObjectFactory,
                                                   SpringInterceptorFactory springInterceptorFactory) {
        // 开始梳理
        ForestConfiguration configuration = ForestConfiguration.configuration();
        configuration.setMaxConnections(this.httpProperties.getMaxConnections());
        configuration.setMaxRouteConnections(this.httpProperties.getMaxRouteConnections());
        configuration.setMaxAsyncThreadSize(this.httpProperties.getMaxAsyncThreadSize());
        configuration.setTimeout(this.httpProperties.getTimeout());
        configuration.setConnectTimeout(this.httpProperties.getConnectTimeout());
        configuration.setReadTimeout(this.httpProperties.getReadTimeout());
        configuration.setCharset(this.httpProperties.getCharset());

        ForestAddress forestAddress = new ForestAddress(this.httpProperties.getAddressScheme(),
                this.httpProperties.getAddressHost(), this.httpProperties.getAddressPort());
        configuration.setBaseAddress(forestAddress);
        configuration.setBaseAddressSourceClass(this.httpProperties.getBaseAddressSourceClass());
        configuration.setRetryer(this.httpProperties.getRetryer());
        configuration.setMaxRetryCount(this.httpProperties.getMaxRetryCount());
        configuration.setMaxRetryInterval(this.httpProperties.getMaxRetryInterval());
        configuration.setAutoRedirection(this.httpProperties.isAutoRedirection());
        configuration.setLogEnabled(this.httpProperties.isLogEnabled());
        configuration.setLogRequest(this.httpProperties.isLogRequest());
        configuration.setLogResponseStatus(this.httpProperties.isLogResponseStatus());
        configuration.setLogResponseContent(this.httpProperties.isLogResponseContent());

        ForestLogHandler forestLogHandler = null;
        try {
            forestLogHandler = this.httpProperties.getLogHandler().newInstance();
        } catch (InstantiationException | IllegalAccessException e) {
            LOGGER.error(e.getMessage());
        }
        configuration.setLogHandler(forestLogHandler);

        configuration.setSslProtocol(this.httpProperties.getSslProtocol());
        configuration.setBackend(this.httpProperties.getHttpBackendSelector().select(this.httpProperties.getBackend()));
        configuration.setInterceptors(this.httpProperties.getInterceptors());
        configuration.setSuccessWhenClass(this.httpProperties.getSuccessWhenClass());
        configuration.setRetryWhenClass(this.httpProperties.getRetryWhenClass());

        configuration.setToMergeConverterMap(this.httpProperties.getConverters());
        configuration.setForestObjectFactory(springForestObjectFactory);
        configuration.setInterceptorFactory(springInterceptorFactory);
        configuration.setProperties(springForestProperties);
        configuration.setHttpBackendSelector(this.httpProperties.getHttpBackendSelector());
        //configuration.registerFilter("",null);
        configuration.setVariables(this.httpProperties.getVariables());

        // SSL
        for (HttpSSLKeyStoreProperties item : this.httpProperties.getSslKeyStores()) {
            SpringSSLKeyStore springSSLKeyStore = new SpringSSLKeyStore(item.getId(), item.getType(),
                    item.getFile(), item.getKeystorePass(), item.getCertPass(),
                    item.getHostnameVerifier(), item.getSslSocketFactoryBuilder());
            if (org.apache.commons.lang3.StringUtils.isNotBlank(item.getProtocols())) {
                String[] tmp = item.getProtocols().split("[ /t]*,[ /t]*");
                String[] protocols = new String[tmp.length];
                for (int i = 0; i < tmp.length; i++) {
                    protocols[i] = tmp[i].trim();
                }
                springSSLKeyStore.setProtocols(protocols);
            }
            if (org.apache.commons.lang3.StringUtils.isNotBlank(item.getCipherSuites())) {
                String[] tmp = item.getCipherSuites().split("[ /t]*,[ /t]*");
                String[] cipherSuites = new String[tmp.length];
                for (int i = 0; i < tmp.length; i++) {
                    cipherSuites[i] = tmp[i].trim();
                }
                springSSLKeyStore.setCipherSuites(cipherSuites);
            }
            configuration.registerKeyStore(springSSLKeyStore);
        }
        return configuration;
    }

    /**
     * 数据类型转换器在刷新期间自动注入
     *
     * @param forestConfiguration forestConfiguration配置
     * @return 数据类型转换器
     */
    @Bean
    public ConverterBeanListener converterBeanListener(ForestConfiguration forestConfiguration) {
        ConverterBeanListener converterBeanListener = new ConverterBeanListener();
        converterBeanListener.setForestConfiguration(forestConfiguration);
        return converterBeanListener;
    }

    /**
     * Class路径扫描器
     *
     * @param forestConfiguration            forestConfiguration配置
     * @param configurableApplicationContext ac上下文
     * @param resourceLoader                 资源加载器
     * @return Class路径扫描器
     */
    @Bean
    public ClassPathClientScanner classPathClientScanner(ForestConfiguration forestConfiguration, ConfigurableApplicationContext configurableApplicationContext,
                                                         ResourceLoader resourceLoader) {
        ClassPathClientScanner classPathClientScanner = new ClassPathClientScanner(forestConfiguration.getId(), (BeanDefinitionRegistry) configurableApplicationContext.getBeanFactory());
        classPathClientScanner.setResourceLoader(resourceLoader);
        classPathClientScanner.doScan(StringUtils.toStringArray(AutoConfigurationPackages.get(configurableApplicationContext)));
        return classPathClientScanner;
    }

}
